{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "\n",
    "本节介绍使用飞桨快速实现“手写数字识别”的建模方法。\n",
    "\n",
    "与“房价预测”的案例类似，我们以同样的标准结构实现“手写数字识别”的建模。在后续的课程中，该标准结构会反复出现，逐渐加深我们对深度学习模型的理解。深度学习模型的标准结构分如下五个步骤：\n",
    "\n",
    "1. 数据处理：读取数据和预处理操作。\n",
    "2. 模型设计：搭建神经网络结构。\n",
    "3. 训练配置：配置优化器、学习率、训练参数。\n",
    "4. 训练过程：循环调用训练过程，循环执行“前向计算 + 损失函数 + 反向传播”。\n",
    "5. 保存模型并测试：将训练好的模型保存并评估测试。\n",
    "\n",
    "下面我们使用飞桨框架，按照五个步骤写“手写数字识别”模型，体会下使用飞桨框架的感觉。\n",
    "\n",
    "在数据处理前，首先要加载飞桨平台、与“手写数字识别”模型相关类库，代码如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "#加载飞桨和相关类库\n",
    "import paddle\n",
    "import paddle.fluid as fluid\n",
    "from paddle.fluid.dygraph.nn import FC\n",
    "import numpy as np\n",
    "import os\n",
    "from PIL import Image"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "# 1. 数据处理\n",
    "\n",
    "飞桨提供了多个封装好的数据集API，覆盖计算机视觉、自然语言处理、推荐系统等多个领域，可以帮助我们快速完成机器学习任务。比如，在“手写数字识别”模型中，我们可以通过调用[paddle.dataset.mnist](https://www.paddlepaddle.org.cn/documentation/docs/zh/api_cn/data/dataset_cn.html)的train函数和test函数，直接获取处理好的MNIST训练集和测试集。\n",
    "\n",
    "## 定义数据读取器\n",
    "\n",
    "用户可以通过如下代码定义数据读取器："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[==================================================]t/train-images-idx3-ubyte.gz not found, downloading https://dataset.bj.bcebos.com/mnist/train-images-idx3-ubyte.gz\n",
      "[==================================================]t/train-labels-idx1-ubyte.gz not found, downloading https://dataset.bj.bcebos.com/mnist/train-labels-idx1-ubyte.gz\n"
     ]
    }
   ],
   "source": [
    "# 如果～/.cache/paddle/dataset/mnist/目录下没有MNIST数据，API会自动将MINST数据下载到该文件夹下\n",
    "# 设置数据读取器，读取MNIST数据训练集\n",
    "trainset = paddle.dataset.mnist.train()\n",
    "# 包装数据读取器，每次读取的数据数量设置为batch_size=8\n",
    "train_reader = paddle.batch(trainset, batch_size=8)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 读取数据，并打印观察\n",
    "\n",
    "paddle.batch函数将MNIST数据集拆分成多个批次，我们可以用下面的代码读取第一个批次的数据内容，并观察数据结果。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "图像数据形状和对应数据为: (8, 784) [-1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -0.9764706  -0.85882354 -0.85882354 -0.85882354\n",
      " -0.01176471  0.06666672  0.37254906 -0.79607844  0.30196083  1.\n",
      "  0.9372549  -0.00392157 -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -0.7647059  -0.7176471  -0.26274508  0.20784318\n",
      "  0.33333337  0.9843137   0.9843137   0.9843137   0.9843137   0.9843137\n",
      "  0.7647059   0.34901965  0.9843137   0.8980392   0.5294118  -0.4980392\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -0.6156863\n",
      "  0.8666667   0.9843137   0.9843137   0.9843137   0.9843137   0.9843137\n",
      "  0.9843137   0.9843137   0.9843137   0.96862745 -0.27058822 -0.35686272\n",
      " -0.35686272 -0.56078434 -0.69411767 -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -0.85882354  0.7176471   0.9843137\n",
      "  0.9843137   0.9843137   0.9843137   0.9843137   0.5529412   0.427451\n",
      "  0.9372549   0.8901961  -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -0.372549    0.22352946 -0.1607843   0.9843137\n",
      "  0.9843137   0.60784316 -0.9137255  -1.         -0.6627451   0.20784318\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -0.8901961  -0.99215686  0.20784318  0.9843137  -0.29411763\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.          0.09019613  0.9843137   0.4901961  -0.9843137  -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -0.9137255\n",
      "  0.4901961   0.9843137  -0.45098037 -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -0.7254902   0.8901961\n",
      "  0.7647059   0.254902   -0.15294117 -0.99215686 -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -0.36470586  0.88235295  0.9843137\n",
      "  0.9843137  -0.06666666 -0.8039216  -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -0.64705884  0.45882356  0.9843137   0.9843137\n",
      "  0.17647064 -0.7882353  -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -0.8745098  -0.27058822  0.9764706   0.9843137   0.4666667\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.          0.9529412   0.9843137   0.9529412  -0.4980392  -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -0.6392157   0.0196079   0.43529415  0.9843137\n",
      "  0.9843137   0.62352943 -0.9843137  -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -0.69411767  0.16078436\n",
      "  0.79607844  0.9843137   0.9843137   0.9843137   0.9607843   0.427451\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -0.8117647  -0.10588235  0.73333335  0.9843137   0.9843137   0.9843137\n",
      "  0.9843137   0.5764706  -0.38823527 -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -0.81960785 -0.4823529   0.67058825  0.9843137\n",
      "  0.9843137   0.9843137   0.9843137   0.5529412  -0.36470586 -0.9843137\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -0.85882354  0.3411765\n",
      "  0.7176471   0.9843137   0.9843137   0.9843137   0.9843137   0.5294118\n",
      " -0.372549   -0.92941177 -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -0.5686275   0.34901965  0.77254903  0.9843137   0.9843137   0.9843137\n",
      "  0.9843137   0.9137255   0.04313731 -0.9137255  -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.          0.06666672  0.9843137\n",
      "  0.9843137   0.9843137   0.6627451   0.05882359  0.03529418 -0.8745098\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.         -1.         -1.\n",
      " -1.         -1.         -1.         -1.        ]\n",
      "图像标签形状和对应数据为: (8,) 5.0\n",
      "\n",
      "打印第一个batch的第一个图像，对应标签数字为5.0\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 以迭代的形式读取数据\n",
    "for batch_id, data in enumerate(train_reader()):\n",
    "    # 获得图像数据，并转为float32类型的数组\n",
    "    img_data = np.array([x[0] for x in data]).astype('float32')\n",
    "    # 获得图像标签数据，并转为float32类型的数组\n",
    "    label_data = np.array([x[1] for x in data]).astype('float32')\n",
    "    # 打印数据形状\n",
    "    print(\"图像数据形状和对应数据为:\", img_data.shape, img_data[0])\n",
    "    print(\"图像标签形状和对应数据为:\", label_data.shape, label_data[0])\n",
    "    break\n",
    "\n",
    "print(\"\\n打印第一个batch的第一个图像，对应标签数字为{}\".format(label_data[0]))\n",
    "# 显示第一batch的第一个图像\n",
    "import matplotlib.pyplot as plt\n",
    "img = np.array(img_data[0]+1)*127.5\n",
    "img = np.reshape(img, [28, 28]).astype(np.uint8)\n",
    "\n",
    "plt.figure(\"Image\") # 图像窗口名称\n",
    "plt.imshow(img)\n",
    "plt.axis('on') # 关掉坐标轴为 off\n",
    "plt.title('image') # 图像题目\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "从代码的输出来看，我们从数据加载器train_loader()中读取一次数据，可以得到形状为 (8, 784)的图像数据和形状为(8,)的标签数据。其中，8与设置的batch大小对应，784为mnist数据集中每个图像的像素数量(28*28)。\n",
    "\n",
    "另外，从打印的图像数据来看，图像数据的范围是[-1, 1]，表明这是已经完成图像归一化后的图像数据，且背景部分的值是-1。我们可以将图像数据反归一化，并使用matplotlib工具包将其显示出来。显示的数字是5，和对应标签数字一致。\n",
    "\n",
    "------\n",
    "**说明：**\n",
    "\n",
    "飞桨将维度是28\\*28的手写数字数据图像转成向量形式存储，因此，使用飞桨数据读取到的手写数字图像是长度为784（28\\*28）的向量。\n",
    "\n",
    "------"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "# 2. 模型设计\n",
    "在“房价预测”深度学习任务中，我们使用了单层且没有非线性变换的模型，取得了理想的预测效果。在“手写数字识别”中，我们依然使用这个模型预测输入的图形数字值。其中，模型的输入为784维（28\\*28）数据，输出为1维数据，如**图1**所示。\n",
    "\n",
    "<center>\n",
    "<img src=\"https://ai-studio-static-online.cdn.bcebos.com/80aea55a15ff4da1bb13a661f26cf8d97b19101e30e44c6fa5f5eb723ce5edb3\" width=\"300\" hegiht=\"200\"></center>\n",
    "\n",
    "<center><br>图1：与“房价预测”模型一样的简单网络</br></center>\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# 定义mnist数据识别网络结构，同房价预测网络\n",
    "class MNIST(fluid.dygraph.Layer):\n",
    "    def __init__(self, name_scope):\n",
    "        super(MNIST, self).__init__(name_scope)\n",
    "        name_scope = self.full_name()\n",
    "        # 定义一层全连接层，输出维度是1，激活函数为None，即不使用激活函数\n",
    "        self.fc = FC(name_scope, size=1, act=None)\n",
    "        \n",
    "    # 定义网络结构的前向计算过程\n",
    "    def forward(self, inputs):\n",
    "        outputs = self.fc(inputs)\n",
    "        return outputs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "# 3. 训练配置\n",
    "\n",
    "训练配置负责神经网络训练前的准备，包括：\n",
    "\n",
    "1. 声明定义好的模型。\n",
    "2. 加载训练数据和测试数据。\n",
    "3. 设置优化算法和学习率，本次实验优化算法使用随机梯度下降SGD，学习率使用 0.01。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# 定义飞桨动态图工作环境\n",
    "with fluid.dygraph.guard():\n",
    "    # 声明网络结构\n",
    "    model = MNIST(\"mnist\")\n",
    "    # 启动训练模式\n",
    "    model.train()\n",
    "    # 定义数据读取函数，数据读取batch_size设置为16\n",
    "    train_loader = paddle.batch(paddle.dataset.mnist.train(), batch_size=16)\n",
    "    # 定义优化器，使用随机梯度下降SGD优化器，学习率设置为0.001\n",
    "    optimizer = fluid.optimizer.SGDOptimizer(learning_rate=0.001)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "# 4. 训练过程\n",
    "\n",
    "完成训练配置后，可启动训练过程。采用二层循环嵌套方式：\n",
    "- 内层循环负责整个数据集的一次遍历，遍历数据集采用分批次（batch）方式。\n",
    "- 外层循环定义遍历数据集的次数，本次训练中外层循环10次，通过参数EPOCH_NUM设置。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "epoch: 0, batch: 1000, loss is: [1.8877316]\n",
      "epoch: 0, batch: 2000, loss is: [3.9273722]\n",
      "epoch: 0, batch: 3000, loss is: [4.084848]\n",
      "epoch: 1, batch: 1000, loss is: [1.854464]\n",
      "epoch: 1, batch: 2000, loss is: [3.8591747]\n",
      "epoch: 1, batch: 3000, loss is: [3.7979107]\n",
      "epoch: 2, batch: 1000, loss is: [1.8508763]\n",
      "epoch: 2, batch: 2000, loss is: [3.8116078]\n",
      "epoch: 2, batch: 3000, loss is: [3.6144927]\n",
      "epoch: 3, batch: 1000, loss is: [1.8574097]\n",
      "epoch: 3, batch: 2000, loss is: [3.790052]\n",
      "epoch: 3, batch: 3000, loss is: [3.484096]\n",
      "epoch: 4, batch: 1000, loss is: [1.8664289]\n",
      "epoch: 4, batch: 2000, loss is: [3.7832546]\n",
      "epoch: 4, batch: 3000, loss is: [3.385687]\n",
      "epoch: 5, batch: 1000, loss is: [1.8755915]\n",
      "epoch: 5, batch: 2000, loss is: [3.7839074]\n",
      "epoch: 5, batch: 3000, loss is: [3.308744]\n",
      "epoch: 6, batch: 1000, loss is: [1.8844205]\n",
      "epoch: 6, batch: 2000, loss is: [3.7880387]\n",
      "epoch: 6, batch: 3000, loss is: [3.247213]\n",
      "epoch: 7, batch: 1000, loss is: [1.8928452]\n",
      "epoch: 7, batch: 2000, loss is: [3.7936094]\n",
      "epoch: 7, batch: 3000, loss is: [3.1972222]\n",
      "epoch: 8, batch: 1000, loss is: [1.9008667]\n",
      "epoch: 8, batch: 2000, loss is: [3.7995973]\n",
      "epoch: 8, batch: 3000, loss is: [3.1561065]\n",
      "epoch: 9, batch: 1000, loss is: [1.9084811]\n",
      "epoch: 9, batch: 2000, loss is: [3.8054972]\n",
      "epoch: 9, batch: 3000, loss is: [3.1219497]\n"
     ]
    }
   ],
   "source": [
    "# 通过with语句创建一个dygraph运行的context，\n",
    "# 动态图下的一些操作需要在guard下进行\n",
    "with fluid.dygraph.guard():\n",
    "    model = MNIST(\"mnist\")\n",
    "    model.train()\n",
    "    train_loader = paddle.batch(paddle.dataset.mnist.train(), batch_size=16)\n",
    "    optimizer = fluid.optimizer.SGDOptimizer(learning_rate=0.001)\n",
    "    EPOCH_NUM = 10\n",
    "    for epoch_id in range(EPOCH_NUM):\n",
    "        for batch_id, data in enumerate(train_loader()):\n",
    "            #准备数据，格式需要转换成符合框架要求的\n",
    "            image_data = np.array([x[0] for x in data]).astype('float32')\n",
    "            label_data = np.array([x[1] for x in data]).astype('float32').reshape(-1, 1)\n",
    "            # 将数据转为飞桨动态图格式\n",
    "            image = fluid.dygraph.to_variable(image_data)\n",
    "            label = fluid.dygraph.to_variable(label_data)\n",
    "            \n",
    "            #前向计算的过程\n",
    "            predict = model(image)\n",
    "            \n",
    "            #计算损失，取一个批次样本损失的平均值\n",
    "            loss = fluid.layers.square_error_cost(predict, label)\n",
    "            avg_loss = fluid.layers.mean(loss)\n",
    "            \n",
    "            #每训练了1000批次的数据，打印下当前Loss的情况\n",
    "            if batch_id !=0 and batch_id  % 1000 == 0:\n",
    "                print(\"epoch: {}, batch: {}, loss is: {}\".format(epoch_id, batch_id, avg_loss.numpy()))\n",
    "            \n",
    "            #后向传播，更新参数的过程\n",
    "            avg_loss.backward()\n",
    "            optimizer.minimize(avg_loss)\n",
    "            model.clear_gradients()\n",
    "\n",
    "    # 保存模型\n",
    "    fluid.save_dygraph(model.state_dict(), 'mnist')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "通过观察训练过程中损失所发生的变化，可以发现虽然损失整体上在降低，但到训练的最后一轮，损失函数值依然较高。可以猜测，“手写数字识别”完全复用“房价预测”的代码，训练效果并不好。接下来我们通过模型测试，获取模型训练的真实效果。\n",
    "\n",
    "# 5. 模型测试\n",
    "\n",
    "\n",
    "模型测试的主要目的是验证训练好的模型是否能正确识别出数字。测试模型包括以下三步：\n",
    "\n",
    "- 从'./demo/example_0.jpg'目录下读取样例图片。\n",
    "- 加载模型并将模型的状态设置为校验状态（eval），显式告诉框架我们接下来只会使用前向计算的流程，不会计算梯度和梯度反向传播，这将减少内存的消耗。\n",
    "- 将测试样本传入模型，获取预测结果，取整后作为预测标签输出。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.0 0.0\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<matplotlib.image.AxesImage at 0x7fb6f799b550>"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 导入图像读取第三方库\n",
    "import matplotlib.image as mpimg\n",
    "import matplotlib.pyplot as plt\n",
    "# 读取图像\n",
    "example = mpimg.imread('./work/example_0.png')\n",
    "# 显示图像\n",
    "plt.imshow(example)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[255 255 255 ... 255 255 255]\n",
      " [255 255 255 ... 255 255 255]\n",
      " [255 255 255 ... 255 255 255]\n",
      " ...\n",
      " [255 255 255 ... 255 255 255]\n",
      " [255 255 255 ... 255 255 255]\n",
      " [255 255 255 ... 255 255 255]]\n",
      "本次预测的数字是 [[1]]\n"
     ]
    }
   ],
   "source": [
    "# 读取一张本地的样例图片，转变成模型输入的格式\n",
    "def load_image(img_path):\n",
    "    # 从img_path中读取图像，并转为灰度图\n",
    "    im = Image.open(img_path).convert('L')\n",
    "    print(np.array(im))\n",
    "    im = im.resize((28, 28), Image.ANTIALIAS)\n",
    "    im = np.array(im).reshape(1, -1).astype(np.float32)\n",
    "    # 图像归一化，保持和数据集的数据范围一致\n",
    "    im = 2 - im / 127.5\n",
    "    return im\n",
    "\n",
    "# 定义预测过程\n",
    "with fluid.dygraph.guard():\n",
    "    model = MNIST(\"mnist\")\n",
    "    params_file_path = 'mnist'\n",
    "    img_path = './work/example_0.png'\n",
    "    # 加载模型参数\n",
    "    model_dict, _ = fluid.load_dygraph(\"mnist\")\n",
    "    model.load_dict(model_dict)\n",
    "    \n",
    "    model.eval()\n",
    "    tensor_img = load_image(img_path)\n",
    "    result = model(fluid.dygraph.to_variable(tensor_img))\n",
    "    #预测输出取整，即为预测的数字\n",
    "    print(\"本次预测的数字是\", result.numpy().astype('int32'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "如上可见，模型错误预测样例图片中的数字是1，实际应该预测的结果是0。但如果我们尝试更多的样本，会发现很多数字图片识别结果是错误的，完全复用房价预测的实验并不适用于手写数字识别任务，接下来我们会对该实验进行逐一改进，直到获得令人满意的结果。\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "## 思考题：\n",
    "\n",
    "1. 使用飞桨API [paddle.dataset.mnist](https://www.paddlepaddle.org.cn/documentation/docs/zh/api_cn/data/dataset_cn.html)的test函数获得测试集数据，计算当前模型的准确率。\n",
    "\n",
    "2. 怎样进一步提高模型的准确率？对于手写数字识别任务，机械的套用“房价预测”的模型是不行的。接下来，我们将逐一考察模型的每个环节可以做出怎样的优化。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "PaddlePaddle 1.6.0 (Python 3.5)",
   "language": "python",
   "name": "py35-paddle1.2.0"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
